package app.kernel.api;

import psg.queryprocessor.QueryPlan;
import psg.queryprocessor.QueryProcessor;
import psg.servicemanager.ServiceManager;
import app.kernel.locator.ComponentsLocator;
import app.kernel.server.PublicServer;
import java.util.Vector;
import java.util.Hashtable;

import config.Static_Context;

import app.kernel.results.*;
// Written By Ng Wen Long McsAPI Version 1.0
public class McsAPI {
	
	
	public ComponentsLocator myDiscoverer;
	String myIP ; // Application IP
	int listeningPort; // Application listening Port
    public QueryProcessor qp;    
    public ServiceManager sm;
    
    public Hashtable<String, Double> minValues = new Hashtable<String, Double>();
    public Hashtable<String, Double> maxValues = new Hashtable<String, Double>();
    
    //public Vector myNeighbours= new Vector(); // Neighbouring Peers Address
    public Hashtable myNB = new Hashtable();
    
    public Hashtable Memory=new Hashtable();
    public Vector QueryQueue = new Vector(); // For query dissemination
    
    public DataRequestTable myRequests = new DataRequestTable(); 
    public String name;
   
    
	public McsAPI(String nam, String IP, int Port) throws Exception{
		
		name = nam;
		myDiscoverer = new ComponentsLocator();
		myIP = IP;
		listeningPort = Port;		
		PublicServer myServer = new PublicServer(Port);
		
		
		sm = Static_Context.getServiceManager(nam, listeningPort);
		qp = new QueryProcessor(sm.cds);
		
		queryForwardingThread newForwardingThread = new queryForwardingThread(nam, QueryQueue, myDiscoverer);
		newForwardingThread.start();
		// monitorContextChangeThread newMonThread = new monitorContextChangeThread(nam, this);
		// newMonThread.start();
		
	}
	
	public static void demo()
	{
		System.out.println("In MCSAPI function");
	}
	
	// - Hash function for integrating all ValueType 
	public double HashValue(String valueType, String Value){
		
		if (valueType.equals("Integer")){

			return new Integer(Value).intValue();
			
		}
		if (valueType.equals("Double")){

			return new Double(Value).doubleValue();
			
		}
		else if (valueType.equals("String")){

			return Value.hashCode();
				
		}
		else
			return -999;
		 
				
	}	
		
	
	public String createSC(String CSG_Name, String SCName, String SC_Gen_Address) throws Exception{
	
		return myDiscoverer.createSC(CSG_Name, SCName, SC_Gen_Address);
	}
	/*
	public String queryPSG(String destinatedAddress ,String queryString, String requestorAddress){
		
		return myDiscoverer.queryPSG(destinatedAddress, queryString, requestorAddress);
		
	}
	*/
	
	
	public double toTimeStamp(String QuerySignature){
		
		String res = QuerySignature.substring(QuerySignature.length()-14, QuerySignature.length()-1);
		
		return (new Double(res)).doubleValue();
		
	}
		
	// Querying the P2P network (From Foreign Calls)
	public String queryP2P(String CSG_Name, String SCName, String nam, String signature, String queryString, String requestorAddress) throws Exception{
		
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Invoked Successfully");
		// Check if the query has been dissemminated from here before.
		//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"GET ADDRESS: "+getAddress());
		/*
		if ((System.currentTimeMillis()-toTimeStamp(signature)) > config.PSG_Configuration.TimeOutForQuery){
			return "[TIME OUT]";			
		}
		*/
		if (Memory.containsKey(signature)){
			//System.out.println("SAME QUERY");
			return "[Same query has been routed to here before]";
		}		
		
		
		Memory.put(signature, new Integer(1));
		
		
		// Add P2P Query to queue.
		// A Thread dispatches these Queries to their destination.
		
		Vector myNeighbours = (Vector)myNB.get(CSG_Name+"."+SCName);
		//System.err.println(myNeighbours);
		if (myNeighbours!=null){
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"MyIP: "+nam);
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"MyNeighbours: "+myNeighbours.size());
			//for (int i=0; i<myNeighbours.size(); i++){
				//System.out.println("[ "+app.io.config.config.resourceName+" ] "+(i+1)+". "+ myNeighbours.get(i));
			//}
			for (int i=0; i<myNeighbours.size(); i++){
			  Query newQuery =new Query(CSG_Name, SCName,(String)myNeighbours.get(i), signature, queryString, requestorAddress); 
			  QueryQueue.add(newQuery);		  
			 // System.out.println("[ "+app.io.config.config.resourceName+" ] "+newQuery);
			}
			
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"-> Query routed to : "+myIP+":"+listeningPort);
		}		
		// Send queries to other neighbours
		String result = queryLocal(queryString);
		//System.err.println(result);
		if (result.equals("[QUERY MISS]")){
		   // Do Nothing
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"QUERY MISS");
		}
		else{ // Query HIT : Report Results back to requestor.
			
			if (!myDiscoverer.existsPSG(requestorAddress)){
				myDiscoverer.get_Outgoing_PSG_Connection(requestorAddress);				
			}	
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"QUERY HIT");
			if (getAddress().equals(requestorAddress)){
				myRequests.reportResultsToLookupRequest(signature, result);
			  
			}
			else {
				myDiscoverer.reportResults(getAddress(), requestorAddress, result, signature, getAddress());
			}
			
		}
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Result = "+ result);
		
	/*	
		for (int i=0; i<myNeighbours.size(); i++){
			
			String neighbourPeerAddress = (String)myNeighbours.get(i);
			// If the random peer connection is not set up, initialize connection.
			if (!myDiscoverer.existsPSG(neighbourPeerAddress)){
				myDiscoverer.get_Outgoing_PSG_Connection(neighbourPeerAddress);				
			}			
			System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Done");
			
		   myDiscoverer.foreignQueryP2P(signature, neighbourPeerAddress, queryString, requestorAddress);
		}*/
        return result;
	}


	// Querying the P2P network (From Foreign Calls)
	public String queryP2P(String CSG_Name, String SCName,String nam, String signature, String queryString, String requestorAddress, int TTL) throws Exception{
		
		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Invoked Successfully");
		// Check if the query has been dissemminated from here before.
		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"GET ADDRESS: "+getAddress());
		
		
		if (Memory.containsKey(signature)){
			return "[Same query has been routed to here before]";
		}		
		
		Memory.put(signature, new Integer(1));
		
		// Add P2P Query to queue.
		// A Thread dispatches these Queries to their destination.
		Vector myNeighbours = (Vector)myNB.get(CSG_Name+"."+SCName);
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"MyNeighbours: "+myNeighbours.size());
		for (int i=0; i<myNeighbours.size(); i++){
		  Query newQuery =new Query(CSG_Name, SCName,(String)myNeighbours.get(i), signature, queryString, requestorAddress, TTL); 
		  QueryQueue.add(newQuery);		  
		  //System.out.println("[ "+app.io.config.config.resourceName+" ] "+newQuery);
		}
		
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"-> Query routed to : "+myIP+":"+listeningPort);
		
		// Send queries to other neighbours
		String result = queryLocal(queryString);
		//System.err.println(result);
		
		if (result.equals("[QUERY MISS]")){
		   // Do Nothing
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"QUERY MISS");
		}
		else{ // Query HIT : Report Results back to requestor.
			
			if (!myDiscoverer.existsPSG(requestorAddress)){
				myDiscoverer.get_Outgoing_PSG_Connection(requestorAddress);				
			}	
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"QUERY HIT");
			if (getAddress().equals(requestorAddress)){
				myRequests.reportResultsToLookupRequest(signature, result);
			  
			}
			else {
				myDiscoverer.reportResults(nam, requestorAddress, result, signature, getAddress());
			}
			
		}
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Result = "+ result);
		
	/*	
		for (int i=0; i<myNeighbours.size(); i++){
			
			String neighbourPeerAddress = (String)myNeighbours.get(i);
			// If the random peer connection is not set up, initialize connection.
			if (!myDiscoverer.existsPSG(neighbourPeerAddress)){
				myDiscoverer.get_Outgoing_PSG_Connection(neighbourPeerAddress);				
			}			
			System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Done");
			
		   myDiscoverer.foreignQueryP2P(signature, neighbourPeerAddress, queryString, requestorAddress);
		}*/
        return result;
	}
		    	

	// Querying the local data
	public String queryLocal(String queryString){
		
		QueryPlan q;		
		q = qp.parseQuery(queryString);
		if (q.errStr!=null){
			  return "[QUERY MISS]";			
			
		}
		else{
			
			if (q.evaluateQueryCondition()){
				
				return q.getQueryResult();
				
			}
			else
				return "[QUERY MISS]";
			
		}		
		
	}
		
//	 Client Side P2P Neighbour Connection Initialization Service
	public String foreignRegisterAsNeighbour(String CSG_Name, String SCName, String nam, String destination) throws Exception{
		
		if (!myDiscoverer.existsPSG(destination)){
			myDiscoverer.get_Outgoing_PSG_Connection(destination);				
		}	
		
		// myDiscoverer.register_Neighbor(nam, destination, getAddress());
		// Added so that the other PSG Simulator knows where to register to. 
		//System.err.println("CSG = "+CSG_Name + "; SC="+SCName+"; Dest="+destination+"getAdd="+getAddress());
		myDiscoverer.register_Neighbor(CSG_Name, SCName,destination, destination, getAddress());
		
		
		return "";
	}
	
	
//	 Server Side P2P Manager Neighbour Connection Initialization Service
	public String registerNeighbour(String CSG_Name, String SCName, String requestorAddress){
		
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"PSG SIM: RegisterNeighbour at "+ myIP+":"+listeningPort+"; Requestor = "+requestorAddress);
		String ID_KEY = CSG_Name +"."+ SCName;
		if (!myNB.containsKey(ID_KEY)){
			myNB.put(ID_KEY, new Vector());
			((Vector)myNB.get(ID_KEY)).add(requestorAddress);
			return "[NEIGHBOUR CONNECTION ESTABLISHED]";
		}
		else{
						
			if ((!((Vector)myNB.get(ID_KEY)).contains(requestorAddress))&&(!requestorAddress.equals(getAddress()))){
				/*System.out.println("[ "+app.io.config.config.resourceName+" ] "+"-> Registering PSG: "+ myIP+":"+listeningPort+" myNB["+ID_KEY+"] SIZE = "+((Vector)myNB.get(ID_KEY)).size());
				for (int i=0; i<((Vector)myNB.get(ID_KEY)).size();i++){
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+((Vector)myNB.get(ID_KEY)).get(i));
				}*/
				((Vector)myNB.get(ID_KEY)).add(requestorAddress);
				
				return "[NEIGHBOUR CONNECTION ESTABLISHED]";
			}
			else
			   return "[NEIGHBOUR CONNECTION REFUSED]";
		}
		
	}
	
	public void ClearAllNeighbours(String CSG_Name, String SCName){
		
		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"ClearConnections: "+listeningPort+" at "+CSG_Name+"."+SCName);
		this.myNB.remove(CSG_Name+"."+SCName);
		myNB.put(CSG_Name+"."+SCName, new Vector());
		
		
	}
	
	// Register itself as a PSG.
	public void leave_PSG(String nam, String CSG_Name, String SCName, String ValueType, String Value) throws Exception{
 
		try {
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<PSG Leaving Request>");
			// Check whether connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			myDiscoverer.get_Outgoing_CP_Connection_System(nam, CSG_Name, SCName, myIP, listeningPort);
			minValues.remove(CSG_Name +"."+SCName);
			maxValues.remove(CSG_Name +"."+SCName);
			String result = myDiscoverer.leave_PSG(ValueType, Value, CSG_Name, SCName, myIP, listeningPort);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Leaving PSG: "+result);
			
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Registration Request>\n");
			//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"SC: "+SCName+" Listening Port: "+listeningPort);
			
		  
		}
		catch (Exception e){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Leaving Failed.");
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Leaving Request>\n");
		}
		
	}		
	
	// Register itself as a PSG.
	public void register_PSG(String ParentCSG,String nam, String CSG_Name, String SCName, String ValueType,String Value) throws Exception{

		try {
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<PSG PARENT Registration Request>");
			// Check whether connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			//myDiscoverer.get_Outgoing_CP_Connection(CSG_Name, SCName, myIP, listeningPort);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"1. CSG CONNECTION GET");
			myDiscoverer.get_Outgoing_CP_Connection_System(nam, ParentCSG+"."+CSG_Name, SCName, myIP, listeningPort);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"2. CP CONNECTION GET");
			messaging.format.MsgExtension MsgExt = new messaging.format.MsgExtension();
			MsgExt.addVariable("name", name);
			MsgExt.addVariable("value", Value);
			MsgExt.addVariable("valueType", ValueType);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"MSG EXT = "+MsgExt.toString());
			
			String getResponse = myDiscoverer.register_PSG(ParentCSG, nam, CSG_Name, SCName, myIP, listeningPort, MsgExt.toString());
			if (getResponse.startsWith("[WAIT FOR SPLIT]")) return;
			
			messaging.format.MsgExtension ReplyExt = new messaging.format.MsgExtension();
			ReplyExt.LoadAdditionalString(getResponse);
			
			String result = ReplyExt.getValue("PeerList");
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Register PSG: Get References: " + result);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Min = "+ReplyExt.getValue("minValue")+"; Max = "+ReplyExt.getValue("maxValue"));
			minValues.put(CSG_Name+"."+SCName, new Double(ReplyExt.getValue("minValue")));
			maxValues.put(CSG_Name+"."+SCName, new Double(ReplyExt.getValue("maxValue")));
			// For Parsing String into multiple neighbours.
			int posi = 0;
			int posi2;
			String getNeighbouringAddress="";								
			while ((posi!=-1) && (posi<result.length()-1)){				
				
				posi2 = result.indexOf(";",posi+1);				
				if (getNeighbouringAddress.equals(""))
					getNeighbouringAddress = result.substring(posi,posi2);
				else 
					getNeighbouringAddress = result.substring(posi+1,posi2);				
				//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"- |"+getNeighbouringAddress+"|");
				
				//myNeighbours.add(getNeighbouringAddress);
				//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Register NB Result: "+);
				registerNeighbour(CSG_Name, SCName, getNeighbouringAddress);
				
				// Set up connection with Neighbouring Peer.
				System.err.println("MCSAPI.ForeignRegisterAsNeighbour; "+"nam="+nam+"; NBAdd="+getNeighbouringAddress);
				foreignRegisterAsNeighbour(CSG_Name, SCName, nam, getNeighbouringAddress);
				posi = posi2;
				
			}
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Registration Request>\n");
			//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"SC: "+SCName+" Listening Port: "+listeningPort);
			
		  
		}
		catch (Exception e){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Registration Failed.");
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Registration Request>\n");
		}
		
	}
	
		
	// Register itself as a PSG.
	public void register_PSG(String nam, String CSG_Name, String SCName, String ValueType,String Value) throws Exception{
		System.out.println("register_PSG");
		/*Check whether the numeric type flag has been set. If yes, then use the mean and variance values to 
		 * register the PSG. Again either modify the existing function to handle this or define a new function that can
		 * take the mean and variance calculation part*/
		try {
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<PSG Registration Request>");
			// Check whether connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			//myDiscoverer.get_Outgoing_CP_Connection(CSG_Name, SCName, myIP, listeningPort);
			myDiscoverer.get_Outgoing_CP_Connection_System(nam, CSG_Name, SCName, myIP, listeningPort);
			messaging.format.MsgExtension MsgExt = new messaging.format.MsgExtension();
			MsgExt.addVariable("name", name);
			MsgExt.addVariable("value", Value);
			MsgExt.addVariable("valueType", ValueType);
			//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"MSG EXT = "+MsgExt.toString());
			System.out.println("Calling register PSG");
			String getResponse = myDiscoverer.register_PSG(nam, CSG_Name, SCName, myIP, listeningPort, MsgExt.toString());
			if (getResponse.startsWith("[WAIT FOR SPLIT]")) return;
			System.out.println("Got response: " + getResponse);
			messaging.format.MsgExtension ReplyExt = new messaging.format.MsgExtension();
			ReplyExt.LoadAdditionalString(getResponse);
			
			String result = ReplyExt.getValue("PeerList");
			
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Register PSG: Get References: " + result);
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Min = "+ReplyExt.getValue("minValue")+"; Max = "+ReplyExt.getValue("maxValue"));
			minValues.put(CSG_Name+"."+SCName, new Double(ReplyExt.getValue("minValue")));
			maxValues.put(CSG_Name+"."+SCName, new Double(ReplyExt.getValue("maxValue")));

			// For Parsing String into multiple neighbours.
			int posi = 0;
			int posi2;
			String getNeighbouringAddress="";								
			while ((posi!=-1) && (posi<result.length()-1)){				
				
				posi2 = result.indexOf(";",posi+1);				
				if (getNeighbouringAddress.equals(""))
					getNeighbouringAddress = result.substring(posi,posi2);
				else 
					getNeighbouringAddress = result.substring(posi+1,posi2);				
			//	System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"- |"+getNeighbouringAddress+"|");
				
				//myNeighbours.add(getNeighbouringAddress);
				//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Register NB Result: "+registerNeighbour(CSG_Name, SCName,getNeighbouringAddress));
				
				// Set up connection with Neighbouring Peer.
				//System.err.println("MCSAPI.RegisterNeighbour; "+"nam="+nam+"; NBAdd="+getNeighbouringAddress);
				//foreignRegisterAsNeighbour(CSG_Name, SCName,nam, getNeighbouringAddress);
				foreignRegisterAsNeighbour(CSG_Name, SCName, getNeighbouringAddress, nam);
				registerNeighbour(CSG_Name, SCName, getNeighbouringAddress);
				
				posi = posi2;
				
			}
			
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Registration Request>\n");
			//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"SC: "+SCName+" Listening Port: "+listeningPort);
			
		  
		}
		catch (Exception e){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Registration Failed.");
			//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</PSG Registration Request>\n");
		}
		
	}

	
	// Register itself as a CP.
	public void register_CP(String CSG_Name, String SCName) throws Exception{

		try {
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<API CP Registration Request>");
			// Check whether connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			myDiscoverer.register_CP(CSG_Name, SCName, myIP, listeningPort);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  SC: "+SCName+" Listening Port: "+listeningPort);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</API CP Registration Request>\n");
		}
		catch (Exception e){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Registration Failed.");
		}
		
	}
	
	//Added by Shubhabrata - Get the session id corresponding to a PSG identifier
	public String getSessionId(String name, String CSG, String IPPort)
	{
		System.out.println("In sessionID function id: " + name);
		String id = myDiscoverer.getSessionId(name, CSG, IPPort);
		return id;
	}
	public void deleteSessionId(String name, String MSID){
		myDiscoverer.deleteSessionId(name, MSID);
	}
	
	
	//Added by Shubhabrata - Register the callback function
	public String registerCallback(String callerId, String calleeId)
	{
		return myDiscoverer.registerCallback(callerId, calleeId);
//		return "Callback registered";
	}
	// Ivan 23 June 2010
	public String withdrawCallback(String callerId, String calleeId)
	{
		return myDiscoverer.withdrawCallback(callerId, calleeId);
//		return "Callback registered";
	}
	public String getIPPort(String MSID)
	{
		return myDiscoverer.getIPPort(MSID);
//		return "Callback registered";
	}
	
	//Added by Shubhabrata - Register the mobility update
	public String mobilityUpdate(String nam,  String sessionId, String IPPort)
	{
		myDiscoverer.mobilityUpdate(nam,  sessionId, IPPort);
		return "Mobility update registered";
	}
		
	public String get_CoordinatorPeer(String nam, String CSG_Name, String SCName){
		
		String result="";

		try{
			
			// Check whether Connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);			

			String querySignature = "["+myIP+":"+listeningPort+"]"+"Get Coordinator Peer"+"["+System.currentTimeMillis()+"]";
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Query Signature: "+ querySignature);			
			// Add a lookup request so that the API can monitor the returned results.
			myRequests.addLookupCP_Request(querySignature);            
			
			if (!myDiscoverer.existsCP(CSG_Name, SCName)){
				
				result = myDiscoverer.get_CoordinatorPeer(nam, CSG_Name, SCName, myIP, listeningPort,querySignature);
				
			}
			else {
				
				result = myDiscoverer.get_CoordinatorPeer(nam, CSG_Name, SCName, myIP, listeningPort, querySignature);
				
			}
			
			return result;
			
			
						
			
			
			
		}
		catch (Exception e){
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Error: Cannot Find Coordinator Peer");
			return "[SC NOT FOUND]";
		}		
		
		//return result;
		
	}
	
	
	
	
	
	
    // Support Cross Domain Query Lookup			
    // Support RDF Query Currently		
	public String queryMCS(String nam, String QueryString){

		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<API Query MCS Request>");
		
		String result="";
		String CSG_Name;
		String SCName;
		
		QueryPlan q;		
		q = qp.parseQuery(QueryString);
		if (q.errStr!=null){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  <INVALID QUERY FORMAT>\n");
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</API Query MCS Request>\n");
			return "[INVALID QUERY FORMAT]";
			
		}
		else{
			
			CSG_Name = q.domain;
			SCName = q.attrs[0];		
			
		}
		
		
		try{
			
			// Check whether Connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			
			if (!myDiscoverer.existsCP(CSG_Name, SCName)){
				myDiscoverer.get_Outgoing_CP_Connection(nam, CSG_Name, SCName, myIP, listeningPort);				
			}	
			
			// The result returns a random peer.
			int numTry=0;
			result = "[QUERY MISS]";
			String querySignature="";
			while (result.equals("[QUERY MISS]")&&(numTry<1)){
				String randomPeerAddress = myDiscoverer.queryMCS(CSG_Name, SCName,QueryString, getAddress());
				System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  => Random Peer Address: " +randomPeerAddress);
				
				if (!randomPeerAddress.equals("[NO PEER]")){
					// If the random peer connection is not set up, initialize connection.
					if (!myDiscoverer.existsPSG(randomPeerAddress)){
						myDiscoverer.get_Outgoing_PSG_Connection(randomPeerAddress);				
					}	
					
					// Return Result to service or application calling it.
					// result = queryLocal(QueryString);
					// queryPSG(randomPeerAddress, QueryString, getAddress());
					
					//String querySignature = "["+randomPeerAddress+"]"+"["+getAddress()+"]"+"["+System.currentTimeMillis()+"]";
					
					querySignature = "["+myIP+":"+listeningPort+"]"+QueryString+"["+System.currentTimeMillis()+"]";
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Query Signature: "+ querySignature);
									
					// Add a lookup request so that the API can monitor the returned results.
					myRequests.addLookupRequest(querySignature);
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Add Lookup ********"+querySignature+"********");
					
					String getResult = myDiscoverer.foreignQueryP2P(CSG_Name, SCName,nam, querySignature,randomPeerAddress,QueryString, getAddress());
					
					
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Query MCS Result: "+ getResult);
					result = getResult;
					
				}
				else {
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Local Query");
					result = queryLocal(QueryString);
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+result);
					numTry=20;
				}
				numTry++;
			}
			
			// if (result.equals("[QUERY MISS]")) Thread.currentThread().sleep(1000);
			
/*			long startTime = System.currentTimeMillis();
			long time=0;
			while (result.equals("[QUERY MISS]") && (myRequests.getResults(querySignature).Size()==0)){
				
				time = System.currentTimeMillis()-startTime;
				
				if (time>2000) return "[QUERY MISS]";
				
			}*/
			int i=0;
			int maxT = config.PSG_Configuration.TimeOutForQuery/20;
			while ((myRequests.getResults(querySignature).Size()==0) &&(i<maxT)){
			
				Thread.currentThread().sleep(20);
				i++;			
			}
			if (i==maxT) return "[QUERY MISS]";
			
			
			return (String)myRequests.getResults(querySignature).getResults().get(0);
			
			
		
		}
		catch (Exception e){
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Error: QueryMCS 3");
		}
		finally{
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</API Query MCS Request>\n");
		}
		return result;
	
		
		
	}

	   // Support Cross Domain Query Lookup			
    // Support RDF Query Currently		
	public String queryMCS(String nam, String QueryString, int TTL){

		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"\n<API Query MCS Request>");
		
		String result="";
		String CSG_Name;
		String SCName;
		
		QueryPlan q;		
		
		double start = System.currentTimeMillis();

		double curr;
		q = qp.parseQuery(QueryString);
		if (q.errStr!=null){
			
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  <INVALID QUERY FORMAT>\n");
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</API Query MCS Request>\n");
			return "[INVALID QUERY FORMAT]";
			
		}
		else{
			
			CSG_Name = q.domain;
			SCName = q.attrs[0];		
			
		}

		curr = (System.currentTimeMillis()-start);
		//app.demo.demo.myComments.add("Parse Query Timing: "+curr+"");
		
		System.out.println("[ "+app.io.config.config.resourceName+" ] "+"===== PARSE QUERY - Time Taken: "+(System.currentTimeMillis()-app.demo.demo.curr)+"ms");
		
		try{
			
			// Check whether Connection has been established with CSG.
			myDiscoverer.get_Outgoing_CSG_Connection(CSG_Name);
			
			curr = (System.currentTimeMillis()-start);
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"===== CSG - Time Taken: "+curr+"");
			
			//app.demo.demo.myComments.add("CSG Lookup Timing: "+curr);
				
			if (!myDiscoverer.existsCP(CSG_Name, SCName)){
				myDiscoverer.get_Outgoing_CP_Connection_System(nam, CSG_Name, SCName, myIP, listeningPort);				
			}	
			curr = (System.currentTimeMillis()-start);
			//app.demo.demo.myComments.add("CP Lookup Timing: "+curr+"");
						
			// The result returns a random peer.
			int numTry=0;
			result = "[QUERY MISS]";
			String querySignature="";
			while (result.equals("[QUERY MISS]")&&(numTry<1)){
				String randomPeerAddress = myDiscoverer.queryMCS(CSG_Name, SCName,QueryString, getAddress());
				String randomPeerAddress1 = myDiscoverer.queryMCS(CSG_Name, SCName,QueryString, getAddress());
				//String randomPeerAddress2 = myDiscoverer.queryMCS(CSG_Name, SCName,QueryString, getAddress());
				//String randomPeerAddress3 = myDiscoverer.queryMCS(CSG_Name, SCName,QueryString, getAddress());
				System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  => Random Peer Address: " +randomPeerAddress);
				
				if (!randomPeerAddress.equals("[NO PEER]")){
					// If the random peer connection is not set up, initialize connection.
					if (!myDiscoverer.existsPSG(randomPeerAddress)){
						myDiscoverer.get_Outgoing_PSG_Connection(randomPeerAddress);				
					}	
					if (!myDiscoverer.existsPSG(randomPeerAddress1)){
						myDiscoverer.get_Outgoing_PSG_Connection(randomPeerAddress1);				
					}	
/*					if (!myDiscoverer.existsPSG(randomPeerAddress2)){
						myDiscoverer.get_Outgoing_PSG_Connection(randomPeerAddress2);				
					}	
					if (!myDiscoverer.existsPSG(randomPeerAddress3)){
						myDiscoverer.get_Outgoing_PSG_Connection(randomPeerAddress3);				
					}*/	
					
					// Return Result to service or application calling it.
					// result = queryLocal(QueryString);
					// queryPSG(randomPeerAddress, QueryString, getAddress());
					
					//String querySignature = "["+randomPeerAddress+"]"+"["+getAddress()+"]"+"["+System.currentTimeMillis()+"]";
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"===== Random PSG - Time Taken: "+(System.currentTimeMillis()-app.demo.demo.curr)+"ms");

					querySignature = "["+myIP+":"+listeningPort+"]"+QueryString+"["+System.currentTimeMillis()+"]";
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Query Signature: "+ querySignature);
									
					// Add a lookup request so that the API can monitor the returned results.
					myRequests.addLookupRequest(querySignature);
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Add Lookup ********"+querySignature+"********");
					
					String getResult = myDiscoverer.foreignQueryP2P(CSG_Name, SCName,nam, querySignature,randomPeerAddress,QueryString, getAddress(),TTL-1);
					String getResult1 = myDiscoverer.foreignQueryP2P(CSG_Name, SCName,nam, querySignature,randomPeerAddress1,QueryString, getAddress(),TTL-1);
//					String getResult2 = myDiscoverer.foreignQueryP2P(nam, querySignature,randomPeerAddress2,QueryString, getAddress());
					//String getResult3 = myDiscoverer.foreignQueryP2P(nam, querySignature,randomPeerAddress3,QueryString, getAddress());
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"===== Foreign Query P2P - Time Taken: "+(System.currentTimeMillis()-app.demo.demo.curr)+"ms");

					//if (!getResult1.equals("QUERY MISS"))
						
					
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Query MCS Result: "+ getResult);
					result = getResult;
					
					
				}
				else {
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+"Local Query");
					result = queryLocal(QueryString);
					System.out.println("[ "+app.io.config.config.resourceName+" ] "+result);
					numTry=20;
				}
				numTry++;
			}
			
			// if (result.equals("[QUERY MISS]")) Thread.currentThread().sleep(1000);
			
/*			long startTime = System.currentTimeMillis();
			long time=0;
			while (result.equals("[QUERY MISS]") && (myRequests.getResults(querySignature).Size()==0)){
				
				time = System.currentTimeMillis()-startTime;
				
				if (time>2000) return "[QUERY MISS]";
				
			}*/
	/*		int i=0;
			int maxT = config.PSG_Configuration.TimeOutForQuery/20;
			while ((myRequests.getResults(querySignature).Size()==0) &&(i<maxT)){
			
				//Thread.currentThread().sleep(20);
				i++;			
			}
			if (i==maxT) return "[QUERY MISS]";*/
			return "[QUERY MISS]";
			
		//	return (String)myRequests.getResults(querySignature).getResults().get(0);
			
			
		
		}
		catch (Exception e){
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"  Error: QueryMCS 3");
		}
		finally{
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</API Query MCS Request>\n");
			System.out.println("[ "+app.io.config.config.resourceName+" ] "+"===== Query MCS - Time Taken: "+(System.currentTimeMillis()-app.demo.demo.curr)+"ms");			
		}
		return result;
	
		
		
	}
	
	
	// Return the address of the MCS Component. 
	public String getAddress(){
		
		return "http://"+myIP+":"+listeningPort+"/xmlrpc";
	}
		
	
}

// This thread monitor the Query Queue which queue up P2P query dissemination requests.
class queryForwardingThread extends Thread{
	
	Vector myQueryQueue;
	ComponentsLocator myDiscoverer;
	int IDN;
	String name;
	public queryForwardingThread(String nam, Vector QueryQueue, ComponentsLocator Discoverer){
		
		myQueryQueue = QueryQueue;
		myDiscoverer = Discoverer;
		name = nam;
		
	}
	
	public void run(){
		
	    while (true){
	    	
	    	try{
	    		
	    		while (myQueryQueue.size()==0){
	    			sleep(1);
	    		}
	    		Query getFirstQuery = (Query)myQueryQueue.get(0);
	    	
	    		
	    		

	    		// TTL Addition
	    		if (getFirstQuery.TTL>0){
		    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"<Query Queue>");
		    		String result = myDiscoverer.foreignQueryP2P(getFirstQuery.CSG_Name, getFirstQuery.SCName,name, getFirstQuery.querySignature, getFirstQuery.destinatedAddress, getFirstQuery.queryString, getFirstQuery.requestorAddress, getFirstQuery.TTL-1); 
		    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+getFirstQuery);
		    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"<Result>"+result+"</Result>");
		    		
		    		while (result.startsWith("Error:")){
		    			
		    			result = myDiscoverer.foreignQueryP2P(getFirstQuery.CSG_Name, getFirstQuery.SCName,name, getFirstQuery.querySignature, getFirstQuery.destinatedAddress, getFirstQuery.queryString, getFirstQuery.requestorAddress, getFirstQuery.TTL-1);
			    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"<Result>"+result+"</Result>");
		    		}
	    		}
	    		// May have problem
	    		myQueryQueue.removeElementAt(0);
	    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"<Removed> Query </Removed>");
	    		//System.out.println("[ "+app.io.config.config.resourceName+" ] "+"</Query Queue>");
	    		
	    		
	    	}
	    	catch (Exception E){
	    		System.out.println("[ "+app.io.config.config.resourceName+" ] "+E);
	    	}
	    }
		
	}
	
	
}


//This thread monitor the Query Queue which queue up P2P query dissemination requests.
class monitorContextChangeThread extends Thread{
	
	McsAPI myDiscoverer;
	int IDN;
	String name;
	Hashtable <String,String> ContextDictionary = new Hashtable <String,String>();
	
	public monitorContextChangeThread(String nam,  McsAPI Discoverer){
		
		myDiscoverer = Discoverer;
		name = nam;

		
	}
	
	public void run(){
		
		
		
	    while (true){
	    	
	    	try{
	    		
	    		if (myDiscoverer.maxValues.size()>0){
		    		Vector <String> ListOfContextVariables = myDiscoverer.sm.getListOfContextVariables();
		    		Hashtable <String, String> valueTypes = myDiscoverer.sm.getContextVariablesTypes();		    		
		    		for (int i=0; i<ListOfContextVariables.size(); i++){
		    			String ContextVariables = ListOfContextVariables.get(i);
		    			if (ContextVariables!=null){
		    						    				
		    				Thread.sleep(10000);
			    			String domain = ContextVariables.substring(0, ContextVariables.indexOf("."));
			    			String SCName  = ContextVariables.substring(ContextVariables.indexOf(".")+1);
			    			String realValue = myDiscoverer.sm.cds.getContext(domain, SCName);
			    			System.out.println("Real Value = "+realValue);		    				

			    			String valueType = valueTypes.get(domain+"."+SCName);
		    				System.out.println("Value Type = "+valueType);		    				

			    			System.out.println("CURRENT VALUES: "+ContextVariables+" = "+realValue);
			    			
			    			double hashRealValue =myDiscoverer.HashValue(valueType, realValue);
			    			//System.out.println("MIN VALUES SIZE = "+ myDiscoverer.minValues.size());
			    			for (int j=0; j<myDiscoverer.minValues.size();j++){
			    				//System.out.println(" KEY[ "+ myDiscoverer.minValues.keySet().toArray()[j] +" ] = " + myDiscoverer.minValues.get(myDiscoverer.minValues.keySet().toArray()[j]));
			    				
			    			}
			    			double minRange = myDiscoverer.minValues.get(domain+"."+SCName);
			    			double maxRange = myDiscoverer.maxValues.get(domain+"."+SCName);
			    			System.out.println("[ Domain = "+domain + "; SCName = "+SCName+" ] = "+hashRealValue);		
			    			System.out.println("[ MinRange = "+minRange+"; MaxRange = "+maxRange+" ]");
			    			// - If still in range,
			    			if ((hashRealValue>=minRange)&&(hashRealValue<=maxRange)){	    				
			    				System.out.println("Still in range");
			    			}	
			    			else{
			    				System.err.println("OUT OF RANGE");
			    				Thread.sleep(10000);
			    				myDiscoverer.sm.re_register(domain, SCName, name);
			    			}
		    			}
		    		}
	    		}
	    			
	    		/*
	    		for (int i=0; i<ListOfContextVariables.size(); i++){
	    			//System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Monitor Context Change Thread");
	    			String ContextVariables = ListOfContextVariables.get(i); 
	    			String domain = ContextVariables.substring(0, ContextVariables.indexOf("."));
	    			String name  = ContextVariables.substring(ContextVariables.indexOf(".")+1);
	    			
	    			String realValue = myDiscoverer.sm.cds.getContext(domain, name);
	    			String valueType = valueTypes.get(domain+"."+name);
	    			if (!ContextDictionary.containsKey(ContextVariables)){
	    				ContextDictionary.put(ListOfContextVariables.get(i), realValue);
	    				System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"ContextDictionary: "+ListOfContextVariables.get(i)+" = "+realValue);
	    			}
	    			else if (!ContextDictionary.get(ContextVariables).equals(realValue)){
	    				System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"ContextDictionary: "+ListOfContextVariables.get(i)+" = "+realValue);
	    				if ((myDiscoverer.HashValue(valueType, realValue)<=myDiscoverer.maxValues.get(domain+"."+name))&&(myDiscoverer.HashValue(valueType, realValue)>=myDiscoverer.minValues.get(domain+"."+name))){
	    					System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Update Values: Changed Value still in range");
	    					ContextDictionary.remove(ListOfContextVariables.get(i));
	    					ContextDictionary.put(ListOfContextVariables.get(i), realValue);
	    				}
	    				else{
	    					System.out.println("[ "+kernel.udp.config.config.resourceName+" ] "+"Update Values: Changed Value "+valueType+"["+realValue+"]"+" not in range");
	    					String previousValue =ContextDictionary.get(ListOfContextVariables.get(i)); 
    						ContextDictionary.remove(ListOfContextVariables.get(i));
	    					ContextDictionary.put(ListOfContextVariables.get(i), realValue);
	    					String ID_KEY = "http://"+myDiscoverer.myIP+ ":"+(myDiscoverer.listeningPort)+"/xmlrpc";
	    					myDiscoverer.sm.leave(ID_KEY, domain, name, previousValue);
	    					myDiscoverer.sm.register(ID_KEY, domain, name, realValue);
	    					
	    				}
	    					
	    			}
	    			
	    			
	    		}*/
	    		Thread.sleep(300);
	    		
	    	}
	    	catch (Exception E){
	    		E.printStackTrace();
	    		System.out.println("[ "+app.io.config.config.resourceName+" ] "+E);
	    	}
	    }
		
	}
	
	
	
	
}


